package com.lyy.king.common.rpc.config;

import com.lyy.king.common.rpc.annotations.EnableLearningApiInterfaceServices;
import com.lyy.king.common.rpc.annotations.LearningApiInterface;
import com.lyy.king.common.rpc.annotations.LearningApiInterfaceScanner;
import org.apache.dubbo.common.utils.StringUtils;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.filter.AnnotationTypeFilter;

import java.util.*;

/**
 * 扫描DubboServiceInterface注解的 Bean，加入到interfaceToImplementation，用于后续注册
 */
public class LearningInterfaceScan implements ImportBeanDefinitionRegistrar {
    public static Map<Class<?>, Class<?>> interfaceToImplementation = new HashMap<>();

    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        // 获取@EnableDubboInterfaceServices注解的basePackages属性值
        Map<String, Object> annotationAttributes = importingClassMetadata.getAnnotationAttributes(EnableLearningApiInterfaceServices.class.getName());
        if (annotationAttributes == null) {
            return;
        }
        // 获取扫描的包路径
        String[] basePackages = (String[]) annotationAttributes.get("basePackages");

        // 扫描basePackages中所有的@DubboServiceInterface接口
        LearningApiInterfaceScanner scannerInterface = new LearningApiInterfaceScanner(false);
        scannerInterface.addIncludeFilter(new AnnotationTypeFilter(LearningApiInterface.class));
        Arrays.stream(basePackages).map(scannerInterface::findCandidateComponents).flatMap(Collection::stream).forEach(bd -> {
            // 获取Class
            Class<?> interfaceClass = getClassByName(bd.getBeanClassName());
            // 扫描所有的bean定义，找到interfaceClass的实现类
            Class<?> beanClass = getInterfaceImplClass(registry, interfaceClass);
            // 实现添加到dubbo待注册列表
            if (Objects.nonNull(beanClass)) {
                interfaceToImplementation.put(interfaceClass, beanClass);
            }
        });
    }

    /**
     * 获取接口的实现类
     *
     * @param registry
     * @param interfaceClass
     * @return
     */
    private Class<?> getInterfaceImplClass(BeanDefinitionRegistry registry, Class interfaceClass) {
        // 从 BeanDefinitionRegistry 获取所有 Bean 的名称
        String[] beanDefinitionNames = registry.getBeanDefinitionNames();

        // 用于记录找到的实现指定接口的 Bean 的数量
        int count = 0;
        Class<?> foundClass = null;

        // 遍历所有 Bean 的名称
        for (String beanDefinitionName : beanDefinitionNames) {
            // 根据 Bean 的名称从 BeanDefinitionRegistry 获取 BeanDefinition
            BeanDefinition beanDefinition = registry.getBeanDefinition(beanDefinitionName);

            Class<?> beanClass;
            try {
                // 从 BeanDefinition 获取 Bean 的类名
                String beanClassName = beanDefinition.getBeanClassName();

                // 如果类名为空，则跳过这个 Bean
                if (beanClassName == null || StringUtils.isBlank(beanClassName)) {
                    continue;
                }

                // 使用类名加载 Bean 的类
                beanClass = Class.forName(beanDefinition.getBeanClassName());
            } catch (ClassNotFoundException e) {
                // 如果加载类时发生错误，则抛出运行时异常
                throw new RuntimeException(e);
            }

            // 检查 Bean 的类是否实现了指定的接口
            if (interfaceClass.isAssignableFrom(beanClass)) {
                // 如果实现了指定的接口，则增加计数器，并记录这个类
                count++;
                foundClass = beanClass;
            }
        }
        // 如果找到的实现指定接口的 Bean 的数量大于 1，那么抛出一个异常
        if (count > 1) {
            throw new IllegalStateException("More than one implementation of the interface found");
        }
        // 如果没有找到实现了指定接口的 Bean，那么返回 null
        // 如果找到了一个实现了指定接口的 Bean，那么返回这个 Bean 的类
        return foundClass;

    }

    /**
     * 根据 class名称获取 Class对象
     *
     * @param className
     * @return
     */
    public Class getClassByName(String className) {
        Class<?> aClass;
        try {
            aClass = Class.forName(className);
        } catch (ClassNotFoundException e) {
            throw new RuntimeException(e);
        }
        return aClass;
    }
}